package com.example.ref;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import com.example.ref.select.Where.OrderBy;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Base64;
import android.util.Log;

public final class RSQLiteDatabaseHelper {
	public static final <T> List<T> select(SQLiteDatabase database,Class<T> _class ,Context context, String SELECT_SCRIPT,List<OrderBy> ORDER_BY,Integer LIMIT) throws InstantiationException, IllegalAccessException, IOException, ClassNotFoundException, InterruptedException
	{
		DatabaseAdapter adapter= null;
		SQLiteDatabase db=null;
		List<T> results = null;
		try
		{
			if(database==null)
			{
				adapter=new DatabaseAdapter( context);
				db=adapter.open();
			}
			else 
				db=database;

			if(ORDER_BY!=null && ORDER_BY.size()>0)
			{
				StringBuilder builder= new StringBuilder();
				builder.append(SELECT_SCRIPT);
				builder.append(" ORDER BY ");
				for (OrderBy oby : ORDER_BY) {
					builder.append(oby.toString());
					builder.append(",");
				}
				SELECT_SCRIPT=builder.toString().substring(0,builder.length()-1);
			}
			if(LIMIT!=null && LIMIT>0)
				SELECT_SCRIPT=SELECT_SCRIPT.concat(" LIMIT ").concat(LIMIT.toString());

			Cursor cursor=db.rawQuery(SELECT_SCRIPT,null);

			int cursorCount=cursor.getCount();

			Log.d("SELECT_SCRIPT",SELECT_SCRIPT);

			if(cursorCount>0)
			{
				cursor.moveToFirst();
				results= new ArrayList<T>(cursor.getCount());
				Field[] fields= _class.getDeclaredFields();
				Class<?> _type;
				T obj;
				int colIndex;
				do
				{
					obj= _class.newInstance();
					colIndex=0;
					for (Field field : fields) 
					{
						if(Modifier.isTransient(field.getModifiers()))
							continue;
						field.setAccessible(true);
						_type= field.getType();
						colIndex=cursor.getColumnIndex(field.getName());
						if(!cursor.isNull(colIndex))
						{
							if(_type== String.class)
								field.set(obj, cursor.getString(colIndex));
							else if(_type== Integer.class || _type== int.class )
								field.set(obj, cursor.getInt(colIndex));
							else if(_type== Long.class || _type== long.class )
								field.set(obj, cursor.getLong(colIndex));
							else if(_type== Float.class || _type== float.class)
								field.set(obj, cursor.getFloat(colIndex));
							else
							{
								String blob=cursor.getString(colIndex);
								Object base64String= fromString(blob);
								field.set(obj, base64String);
							}
						}
					}

					results.add(obj);
				}while(cursor.moveToNext());
			}


			cursor.close();
		}
		finally
		{
			if(adapter!=null && db!=null)
				adapter.close();
		}
		return results;
	}



	public static  final int delete(SQLiteDatabase database, Class<?> _class,String whereString,Context ctx)
	{
		DatabaseAdapter adapter= null;
		SQLiteDatabase db=null;
		Cursor cursor=null;
		try
		{	
			if(database==null)
			{
				adapter=new DatabaseAdapter(ctx);
				db=adapter.open();
			}
			else 
				db=database;

			StringBuilder delete= new StringBuilder();
			delete.append("DELETE FROM [");
			delete.append(_class.getName());
			delete.append("]");
			if(whereString!=null && whereString.length()>0)
			{
				delete.append(whereString);
			}
			Log.d("DELETE_SCRIPT", delete.toString());
			db.execSQL(delete.toString());
			return 1; //temp
		}
		catch(Exception ex)
		{
			return -1;
		}
		finally
		{
			if(cursor!=null)
				cursor.close();
			if(adapter!=null && db!=null)
				adapter.close();
		}
	}

	public static  final <T>   void insert(T o,Context context)
	{
		insertInto(o,context);
	}
	public static final <T>  void insert(SQLiteDatabase adapter,T o,Context context)
	{
		insertInto(adapter,o,context);
	}
	static final void createTable(Class<?> _class,SQLiteDatabase db)
	{
		db.execSQL(createTableSQL(_class));
	}
	static final  void createTable(Class<?> _class,Context context)
	{

		DatabaseAdapter adapter= new DatabaseAdapter( context);
		SQLiteDatabase db=null;
		try
		{
			db=adapter.open();
			final String ONCREATE_SCRIPT=createTableSQL(_class);
			db.execSQL(ONCREATE_SCRIPT);
		}
		finally
		{
			if(adapter!=null && db!=null)
				adapter.close();
		}



	}

	private final static  void insertInto(SQLiteDatabase database,Object o,Context context) 
	{

		if(database==null)
			createTable(o.getClass(),context);
		else 
			createTable(o.getClass(), database);

		Class<?>_class= o.getClass();
		Field[] fields= _class.getDeclaredFields();
		StringBuilder values= new StringBuilder();
		StringBuilder builder= new StringBuilder();
		builder.append("INSERT INTO [");
		builder.append(_class.getName());
		builder.append("] (");

		for(int i=0;i<fields.length;i++)
		{
			Field f= fields[i];

			if(Modifier.isTransient(f.getModifiers()))
				continue;

			builder.append("[");
			builder.append(f.getName());
			builder.append("] ");			
			if(i+1<fields.length)
				builder.append(",");	

			f.setAccessible(true);
			try
			{
				Object value=f.get(o);
				Type type=f.getType(); 

				appendWithOrWithoutQuotes(values,value,type);

				if(i+1<fields.length)
					values.append(",");	
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}

		builder.append(") VALUES (");

		builder.append(values.toString());

		builder.append(");");

		final String INSERT_SCRIPT=builder.toString();
		if(database==null)
		{
			DatabaseAdapter adapter= new DatabaseAdapter(context);
			SQLiteDatabase db=null;
			try
			{
				db=adapter.open();
				db.execSQL(INSERT_SCRIPT);
			}
			finally
			{
				if(adapter!=null && db!=null)
					adapter.close();
			}
		}
		else
		{
			database.execSQL(INSERT_SCRIPT);
		}
		Log.d("INSERT_SCRIPT",INSERT_SCRIPT);
	}


	private final static void insertInto(Object o,Context context) 
	{
		insertInto(null,o,context);
	}

	public  static  void dropTable(Class<?> _class,Context context) {
		DatabaseAdapter adapter= new DatabaseAdapter(context);
		SQLiteDatabase db=null;
		try
		{
			db=adapter.open();
			db.execSQL(dropTableSQL(_class));
		}
		finally
		{
			if(adapter!=null && db!=null)
				adapter.close();
		}
	}
	public  static final String dropTableSQL(Class<?>_class)
	{
		StringBuilder builder= new StringBuilder();
		builder.append("DROP TABLE IF EXISTS [");
		builder.append(_class.getName());
		builder.append("];");
		return builder.toString();
	}

	public  static final <T> String createTableSQL(Class<T>_class)
	{
		Field[] fields= _class.getDeclaredFields();
		if(fields!=null && fields.length>0)
		{
			StringBuilder builder= new StringBuilder();
			builder.append("CREATE TABLE IF NOT EXISTS [");
			builder.append(_class.getName());
			builder.append("] ( ");
			builder.append(" [___ID] INTEGER PRIMARY KEY, ");
			for(int i=0;i<fields.length;i++)
			{
				Field f= fields[i];
				if(Modifier.isTransient(f.getModifiers()))
					continue;
				builder.append("[");
				builder.append(f.getName());
				builder.append("] ");			
				builder.append(getSqlTypeFromClass(f.getType()));
				if(i+1<fields.length)
					builder.append(",");			
			}
			builder.append(" ); ");
			return builder.toString();
		}
		return "";
	}
	static final void appendWithOrWithoutQuotes(StringBuilder builder, Object value, Type type) throws IOException
	{
		appendWithOrWithoutQuotes(builder,value,type,false);
	}
	public final static void appendWithOrWithoutQuotes(StringBuilder builder, Object value, Type type,boolean noQuotes) throws IOException
	{
		String typeString= getSqlTypeFromClass(type);

		if(typeString.startsWith("I") || typeString.startsWith("D") || typeString.startsWith("F") )
		{
			if(value==null)
				value=0;
			builder.append(value.toString());
		}
		else if(typeString.startsWith("T") && value!=null && value.getClass()!=String.class)
		{		
			if(!noQuotes)
				builder.append("'");
			builder.append(toString(value));
			if(!noQuotes)
				builder.append("'");
		}
		else
		{		
			if(value==null)
				value="";
			if(!noQuotes)
				builder.append("'");
			builder.append(value.toString());
			if(!noQuotes)
				builder.append("'");
		}
	}
	static final String getSqlTypeFromClass(Type type)
	{
		if(Integer.class==type || int.class==type)
			return "INTEGER";
		else if(Double.class==type || double.class==type )
			return "DOUBLE";
		else if(Long.class==type || long.class==type )
			return "LONG";
		else if(Float.class==type || float.class==type )
			return "FLOAT";

		return "TEXT";

	}
	public final static void throwIfFieldDoesntExists(Field[] fields,String stringField) throws Exception
	{
		boolean found=false;
		for (Field field : fields) {
			if(field.getName().toLowerCase().trim().equals(stringField.toLowerCase().trim()))
			{
				found=true;
				break;
			}
		}
		if(!found)
			throw new Exception("Field "+ stringField +"not found");
	}
	private final static Object fromString( String s ) throws IOException ,
	ClassNotFoundException {
		byte [] data = Base64.decode( s ,Base64.DEFAULT);
		ObjectInputStream ois = new ObjectInputStream( 
				new ByteArrayInputStream(  data ) );
		Object o  = ois.readObject();
		ois.close();
		return o;
	}

	/** Write the object to a Base64 string. */
	private final static String toString( Object o ) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream( baos );
		oos.writeObject( o );
		oos.close();
		return new String( Base64.encode( baos.toByteArray(),Base64.DEFAULT ) );
	}


	public final static int count(Class<?> _class,StringBuilder builder, Context context)
	{
		String FIRSTPART_OF_STRING= "SELECT COUNT(*) as cnt ";
		String SECONDPART_OF_STRING= builder.toString().substring(builder.toString().indexOf("FROM ["));
		String SQL_STRING= FIRSTPART_OF_STRING.concat(SECONDPART_OF_STRING);

		DatabaseAdapter adapter= new DatabaseAdapter(context);
		SQLiteDatabase db=null;
		Cursor cursor=null;
		try
		{

			db=adapter.open();
			Log.d("COUNT_SCRIPT",SQL_STRING);
			cursor=db.rawQuery(SQL_STRING,null);
			cursor.moveToFirst();
			return cursor.getInt(cursor.getColumnIndex("cnt"));

		} catch (Exception e) {
			return -1;
		}
		finally
		{
			if(cursor!=null)
				cursor.close();
			if(adapter!=null && db!=null)
				adapter.close();
		}
	}

}
